## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## Loading required package: carData
##
## Attaching package: 'car'
##
## The following object is masked from 'package:dplyr':
##
## recode
##
## The following object is masked from 'package:purrr':
##
## some
##
## Attaching package: 'scales'
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
##
## Attaching package: 'kableExtra'
##
## The following object is masked from 'package:dplyr':
##
## group_rows
## Rows: 49244 Columns: 35
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): TaskID, ActionID, distance, direction, complexity, zoomDirection,...
## dbl (22): UserID, main_translation_x, main_translation_y, main_translation_...
## lgl (3): rotateGlobeWhileDragging, oneHandedRotationGesture, moveGlobeWhil...
## dttm (1): Date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): Timestamp, Academic_level, Gender, Age_group, Exp_ARVR, Globe_usage...
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Positioning_preference, Positioning_feedback
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Rotation_preference, Rotation_feedback
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Scale_preference, Scale_feedback
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): Timestamp, Combined_positioning_preference, Combined_rotation_prefe...
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## UserID TaskID ActionID
## Min. : 1.000 Length:49244 Length:49244
## 1st Qu.: 4.000 Class :character Class :character
## Median : 7.000 Mode :character Mode :character
## Mean : 6.741
## 3rd Qu.:10.000
## Max. :12.000
## rotateGlobeWhileDragging oneHandedRotationGesture moveGlobeWhileScaling
## Mode :logical Mode :logical Mode :logical
## FALSE:36803 FALSE:11933 FALSE:46552
## TRUE :12441 TRUE :37311 TRUE :2692
##
##
##
## distance direction complexity zoomDirection
## Length:49244 Length:49244 Length:49244 Length:49244
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Date Type ActionStatus
## Min. :2025-04-23 05:27:13.00 Length:49244 Length:49244
## 1st Qu.:2025-04-25 01:36:58.00 Class :character Class :character
## Median :2025-04-26 00:45:01.00 Mode :character Mode :character
## Mean :2025-04-27 21:46:53.98
## 3rd Qu.:2025-05-01 07:26:51.00
## Max. :2025-05-05 23:37:33.00
## main_translation_x main_translation_y main_translation_z main_rotation_x
## Min. :-7.099065 Min. :-0.3298 Min. :-3.487 Min. :-0.97540
## 1st Qu.:-0.400000 1st Qu.: 0.9000 1st Qu.:-1.921 1st Qu.:-0.03161
## Median :-0.004060 Median : 0.9000 Median :-1.500 Median : 0.00000
## Mean :-0.005048 Mean : 1.2326 Mean :-1.683 Mean :-0.03896
## 3rd Qu.: 0.400000 3rd Qu.: 1.5539 3rd Qu.:-1.500 3rd Qu.: 0.00000
## Max. : 3.256168 Max. : 3.8304 Max. : 5.006 Max. : 0.97834
## main_rotation_y main_rotation_z main_rotation_w main_scale_x
## Min. :-1.0000 Min. :-0.97710 Min. :-0.9997261 Min. :0.08431
## 1st Qu.:-0.2033 1st Qu.: 0.00000 1st Qu.: 0.0000001 1st Qu.:0.99989
## Median : 0.9601 Median : 0.00000 Median : 0.0626987 Median :1.00000
## Mean : 0.5003 Mean : 0.01287 Mean : 0.2756917 Mean :0.99575
## 3rd Qu.: 1.0000 3rd Qu.: 0.00000 3rd Qu.: 0.6346812 3rd Qu.:1.00002
## Max. : 1.0000 Max. : 0.98922 Max. : 0.9999814 Max. :7.69231
## main_scale_y main_scale_z target_translation_x target_translation_y
## Min. :0.08431 Min. :0.08431 Min. :-3.10000 Min. :0.613
## 1st Qu.:0.99994 1st Qu.:0.99990 1st Qu.:-0.40000 1st Qu.:0.900
## Median :1.00000 Median :1.00000 Median : 0.00000 Median :0.900
## Mean :0.99577 Mean :0.99576 Mean :-0.02449 Mean :1.245
## 3rd Qu.:1.00002 3rd Qu.:1.00002 3rd Qu.: 0.40000 3rd Qu.:1.773
## Max. :7.69231 Max. :7.69231 Max. : 2.33777 Max. :2.547
## target_translation_z target_rotation_x target_rotation_y target_rotation_z
## Min. :-3.3210 Min. :-0.3928 Min. :-0.6935 Min. :-0.21194
## 1st Qu.:-1.9598 1st Qu.:-0.3584 1st Qu.:-0.5655 1st Qu.: 0.00000
## Median :-1.5000 Median : 0.0000 Median : 1.0000 Median : 0.00000
## Mean :-1.6971 Mean :-0.1153 Mean : 0.3768 Mean :-0.01644
## 3rd Qu.:-1.5000 3rd Qu.: 0.0000 3rd Qu.: 1.0000 3rd Qu.: 0.00000
## Max. :-0.8953 Max. : 0.0000 Max. : 1.0000 Max. : 0.13795
## target_rotation_w target_scale_x target_scale_y target_scale_z
## Min. :-0.9761015 Min. :0.1700 Min. :0.1700 Min. :0.1700
## 1st Qu.: 0.0000001 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000
## Median : 0.0000001 Median :1.0000 Median :1.0000 Median :1.0000
## Mean : 0.2914215 Mean :0.9946 Mean :0.9946 Mean :0.9946
## 3rd Qu.: 0.7119398 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. : 0.9807853 Max. :2.0000 Max. :2.0000 Max. :2.0000
## match_accuracy_result status
## Min. : 0.00000 Length:49244
## 1st Qu.: 0.00000 Class :character
## Median : 0.00000 Mode :character
## Mean : 0.03784
## 3rd Qu.: 0.00000
## Max. :22.31002
## UserID Timestamp Academic_level Gender
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## Age_group Exp_ARVR Globe_usage_frequency
## Length:12 Length:12 Length:12
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
## Have_used_VisionPro
## Length:12
## Class :character
## Mode :character
##
##
##
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Positioning_preference Positioning_feedback
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Rotation_preference Rotation_feedback
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Scale_preference Scale_feedback
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Combined_positioning_preference
## Min. : 1.00 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character
## Median : 6.50 Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## Combined_rotation_preference Combined_scale_preference Combined_feedback
## Length:12 Length:12 Length:12
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
Restructure the data for better analysis
# Positioning
data.positioning.accuracy <- data %>%
filter(Type == "positionTask") %>%
select(UserID, TaskID, rotateGlobeWhileDragging, match_accuracy_result, status) %>%
filter(status == "Matched") %>%
select(-status) %>%
mutate(Technique = if_else(rotateGlobeWhileDragging, "rotatingGlobe", "nonRotatingGlobe")) %>%
select(-rotateGlobeWhileDragging) %>%
rename("Accuracy" = "match_accuracy_result")
data.positioning.time <- data %>%
filter(Type == "positionTask") %>%
select(UserID, TaskID, rotateGlobeWhileDragging, Date) %>%
group_by(UserID, TaskID, rotateGlobeWhileDragging) %>%
summarise(
Time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
mutate(Technique = if_else(rotateGlobeWhileDragging, "rotatingGlobe", "nonRotatingGlobe")) %>%
select(-rotateGlobeWhileDragging)
data.positioning.borg <- data %>%
filter(Type == "positionTask") %>%
mutate(Technique = if_else(rotateGlobeWhileDragging, "rotatingGlobe", "nonRotatingGlobe")) %>%
select(UserID, Technique) %>%
distinct() %>%
inner_join(positioning_NRG, by = "UserID") %>%
rename(BORG_NRG = Physically_demanding) %>%
select(-Timestamp, -Mentally_demanding) %>%
mutate(BORG_NRG = as.numeric(str_extract(BORG_NRG, "\\d+(\\.\\d+)?"))) %>%
inner_join(positioning_RG, by = "UserID") %>%
rename(BORG_RG = Physically_demanding) %>%
select(-Timestamp, -Mentally_demanding) %>%
mutate(BORG_RG = as.numeric(str_extract(BORG_RG, "\\d+(\\.\\d+)?"))) %>%
mutate(
BORGRPE = if_else(Technique == "rotatingGlobe", BORG_RG, BORG_NRG)
) %>%
select(-BORG_RG, -BORG_NRG)
data.positioning.paas <- data %>%
filter(Type == "positionTask") %>%
mutate(Technique = if_else(rotateGlobeWhileDragging, "rotatingGlobe", "nonRotatingGlobe")) %>%
select(UserID, Technique) %>%
distinct() %>%
inner_join(positioning_NRG, by = "UserID") %>%
rename(PAAS_NRG = Mentally_demanding) %>%
select(-Timestamp, -Physically_demanding) %>%
mutate(PAAS_NRG = as.numeric(str_extract(PAAS_NRG, "\\d+(\\.\\d+)?"))) %>%
inner_join(positioning_RG, by = "UserID") %>%
rename(PAAS_RG = Mentally_demanding) %>%
select(-Timestamp, -Physically_demanding) %>%
mutate(PAAS_RG = as.numeric(str_extract(PAAS_RG, "\\d+(\\.\\d+)?"))) %>%
mutate(
PAAS = if_else(Technique == "rotatingGlobe", PAAS_RG, PAAS_NRG)
) %>%
select(-PAAS_RG, -PAAS_NRG)
data.positioning.qualitative <- positioning_preference %>%
select(-Timestamp) %>%
mutate(
Positioning_preference = case_when(
str_detect(Positioning_preference, "Static orientation") ~ "staticOrientation",
str_detect(Positioning_preference, "Adaptive orientation") ~ "adaptiveOrientation",
str_detect(Positioning_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
),
Positioning_preference = as.factor(Positioning_preference)
)
# Rotating
data.rotating.accuracy <- data %>%
filter(Type == "rotationTask") %>%
select(UserID, TaskID, oneHandedRotationGesture, match_accuracy_result, status) %>%
filter(status == "Matched") %>%
select(-status) %>%
mutate(Technique = if_else(oneHandedRotationGesture, "oneHanded", "twoHanded")) %>%
select(-oneHandedRotationGesture) %>%
rename("Accuracy" = "match_accuracy_result")
data.rotating.time <- data %>%
filter(Type == "rotationTask") %>%
select(UserID, TaskID, oneHandedRotationGesture, Date) %>%
group_by(UserID, TaskID, oneHandedRotationGesture) %>%
summarise(
Time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
mutate(Technique = if_else(oneHandedRotationGesture, "oneHanded", "twoHanded")) %>%
select(-oneHandedRotationGesture)
data.rotating.borg <- data %>%
filter(Type == "rotationTask") %>%
mutate(Technique = if_else(oneHandedRotationGesture, "oneHanded", "twoHanded")) %>%
select(UserID, Technique) %>%
distinct() %>%
inner_join(rotation_OH, by = "UserID") %>%
rename(BORG_OH = Physically_demanding) %>%
select(-Timestamp, -Mentally_demanding) %>%
mutate(BORG_OH = as.numeric(str_extract(BORG_OH, "\\d+(\\.\\d+)?"))) %>%
inner_join(rotation_TH, by = "UserID") %>%
rename(BORG_TH = Physically_demanding) %>%
select(-Timestamp, -Mentally_demanding) %>%
mutate(BORG_TH = as.numeric(str_extract(BORG_TH, "\\d+(\\.\\d+)?"))) %>%
mutate(
BORGRPE = if_else(Technique == "oneHanded", BORG_OH, BORG_TH)
) %>%
select(-BORG_OH, -BORG_TH)
data.rotating.paas <- data %>%
filter(Type == "rotationTask") %>%
mutate(Technique = if_else(oneHandedRotationGesture, "oneHanded", "twoHanded")) %>%
select(UserID, Technique) %>%
distinct() %>%
inner_join(rotation_OH, by = "UserID") %>%
rename(PAAS_OH = Mentally_demanding) %>%
select(-Timestamp, -Physically_demanding) %>%
mutate(PAAS_OH = as.numeric(str_extract(PAAS_OH, "\\d+(\\.\\d+)?"))) %>%
inner_join(rotation_TH, by = "UserID") %>%
rename(PAAS_TH = Mentally_demanding) %>%
select(-Timestamp, -Physically_demanding) %>%
mutate(PAAS_TH = as.numeric(str_extract(PAAS_TH, "\\d+(\\.\\d+)?"))) %>%
mutate(
PAAS = if_else(Technique == "oneHanded", PAAS_OH, PAAS_TH)
) %>%
select(-PAAS_OH, -PAAS_TH)
data.rotating.qualitative <- rotation_preference %>%
select(-Timestamp) %>%
mutate(
Rotation_preference = case_when(
str_detect(Rotation_preference, "One-handed") ~ "oneHandedPreference",
str_detect(Rotation_preference, "Two-handed") ~ "twoHandedPreference",
str_detect(Rotation_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
),
Rotation_preference = as.factor(Rotation_preference)
)
# Scale
data.scale.accuracy <- data %>%
filter(Type == "scaleTask") %>%
select(UserID, TaskID, moveGlobeWhileScaling, match_accuracy_result, status) %>%
filter(status == "Matched") %>%
select(-status) %>%
mutate(Technique = if_else(moveGlobeWhileScaling, "movingGlobe", " nonMovingGlobe")) %>%
select(-moveGlobeWhileScaling) %>%
rename("Accuracy" = "match_accuracy_result")
data.scale.time <- data %>%
filter(Type == "scaleTask") %>%
select(UserID, TaskID, moveGlobeWhileScaling, Date) %>%
group_by(UserID, TaskID, moveGlobeWhileScaling) %>%
summarise(
Time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
mutate(Technique = if_else(moveGlobeWhileScaling, "movingGlobe", "nonMovingGLobe")) %>%
select(-moveGlobeWhileScaling)
data.scale.borg <- data %>%
filter(Type == "scaleTask") %>%
mutate(Technique = if_else(moveGlobeWhileScaling, "movingGlobe", "nonMovingGlobe")) %>%
select(UserID, Technique) %>%
distinct() %>%
inner_join(scale_NMG, by = "UserID") %>%
rename(BORG_NMG = Physically_demanding) %>%
select(-Timestamp, -Mentally_demanding) %>%
mutate(BORG_NMG = as.numeric(str_extract(BORG_NMG, "\\d+(\\.\\d+)?"))) %>%
inner_join(scale_MG, by = "UserID") %>%
rename(BORG_MG = Physically_demanding) %>%
select(-Timestamp, -Mentally_demanding) %>%
mutate(BORG_MG = as.numeric(str_extract(BORG_MG, "\\d+(\\.\\d+)?"))) %>%
mutate(
BORGRPE = if_else(Technique == "movingGlobe", BORG_MG, BORG_NMG)
) %>%
select(-BORG_MG, -BORG_NMG)
data.scale.paas <- data %>%
filter(Type == "scaleTask") %>%
mutate(Technique = if_else(moveGlobeWhileScaling, "movingGlobe", "nonMovingGlobe")) %>%
select(UserID, Technique) %>%
distinct() %>%
inner_join(scale_NMG, by = "UserID") %>%
rename(PAAS_NMG = Mentally_demanding) %>%
select(-Timestamp, -Physically_demanding) %>%
mutate(PAAS_NMG = as.numeric(str_extract(PAAS_NMG, "\\d+(\\.\\d+)?"))) %>%
inner_join(scale_MG, by = "UserID") %>%
rename(PAAS_MG = Mentally_demanding) %>%
select(-Timestamp, -Physically_demanding) %>%
mutate(PAAS_MG = as.numeric(str_extract(PAAS_MG, "\\d+(\\.\\d+)?"))) %>%
mutate(
PAAS = if_else(Technique == "movingGlobe", PAAS_MG, PAAS_NMG)
) %>%
select(-PAAS_MG, -PAAS_NMG)
data.scale.qualitative <- scale_preference %>%
select(-Timestamp) %>%
mutate(
Scale_preference = case_when(
str_detect(Scale_preference, "Maintain distance") ~ "maintainDistance",
str_detect(Scale_preference, "Maintain globe") ~ "maintainGlobe",
str_detect(Scale_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
),
Scale_preference = as.factor(Scale_preference)
)
# Combined qualitative
data.combined.qualitative <- combined_preference %>%
select(-Timestamp) %>%
rename(
Positioning_preference = Combined_positioning_preference,
Rotation_preference = Combined_rotation_preference,
Scale_preference = Combined_scale_preference
) %>%
mutate(
Positioning_preference = case_when(
str_detect(Positioning_preference, "Static orientation") ~ "staticOrientation",
str_detect(Positioning_preference, "Adaptive orientation") ~ "adaptiveOrientation",
str_detect(Positioning_preference, "No preference") ~ "noPreference",
TRUE ~ "unknown"
),
Positioning_preference = as.factor(Positioning_preference)
) %>%
mutate(
Rotation_preference = case_when(
str_detect(Rotation_preference, "One-handed") ~ "oneHandedPreference",
str_detect(Rotation_preference, "Two-handed") ~ "twoHandedPreference",
str_detect(Rotation_preference, "No preference") ~ "noPreference",
TRUE ~ "unknown"
),
Rotation_preference = as.factor(Rotation_preference)
) %>%
mutate(
Scale_preference = case_when(
str_detect(Scale_preference, "Maintain distance") ~ "maintainDistance",
str_detect(Scale_preference, "Maintain globe") ~ "maintainGlobe",
str_detect(Scale_preference, "No preference") ~ "noPreference",
TRUE ~ "unknown"
),
Scale_preference = as.factor(Scale_preference)
)## [1] 12
# Participants' gender distribution
demographic.gender <- demographic %>%
select(UserID, Gender) %>%
distinct() %>%
group_by(Gender) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1), percentage = paste0(percentage, "%"))
demographic.gender## # A tibble: 2 × 3
## Gender count percentage
## <chr> <int> <chr>
## 1 Man 10 83.3%
## 2 Woman 2 16.7%
# Participants' gender distribution chart
ggplot(demographic.gender, aes(x = "", y = count, fill = Gender)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = percentage), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants' Gender") +
theme_void()# Participants' academic level distribution
demographic.academic_level <- demographic %>%
select(UserID, Academic_level) %>%
distinct() %>%
group_by(Academic_level) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1), graph_label = paste0(percentage, "%")) %>%
rename(`Academic levels` = Academic_level)
demographic.academic_level## # A tibble: 3 × 4
## `Academic levels` count percentage graph_label
## <chr> <int> <dbl> <chr>
## 1 Graduate Student 10 83.3 83.3%
## 2 Postdoctoral Researcher 1 8.3 8.3%
## 3 Undergraduate Student 1 8.3 8.3%
# Participants' academic level distribution chart
ggplot(demographic.academic_level, aes(x = "", y = count, fill = `Academic levels`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = graph_label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants' Academic Level") +
theme_void() # Participants' previous AR/VR experience distribution
demographic.ARVR_exp <- demographic %>%
select(UserID, Exp_ARVR ) %>%
distinct() %>%
group_by(Exp_ARVR) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1),
label = paste0(percentage, "%"),
ShortLabel = fct_recode(Exp_ARVR,
"No experience" = "I have no experience")
) %>%
rename(`Previous AR/VR experience` = ShortLabel)
demographic.ARVR_exp## # A tibble: 3 × 5
## Exp_ARVR count percentage label Previous AR/VR exper…¹
## <chr> <int> <dbl> <chr> <fct>
## 1 Beginner (less than 5 hours exp… 4 33.3 33.3% Beginner (less than 5…
## 2 Familiar (5-20 hours experience) 3 25 25% Familiar (5-20 hours …
## 3 I have no experience 5 41.7 41.7% No experience
## # ℹ abbreviated name: ¹`Previous AR/VR experience`
# Participants' previous AR/VR experience distribution chart
ggplot(demographic.ARVR_exp, aes(x = "", y = count, fill = `Previous AR/VR experience`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants Previous AR/VR Experience") +
theme_void() # Participants' previous globe experience distribution
demographic.globes_exp <- demographic %>%
select(UserID, Globe_usage_frequency) %>%
distinct() %>%
group_by(Globe_usage_frequency) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1),
graph_label = paste0(percentage, "%")) %>%
rename(`Previous globes experience` = Globe_usage_frequency)
demographic.globes_exp## # A tibble: 3 × 4
## `Previous globes experience` count percentage graph_label
## <chr> <int> <dbl> <chr>
## 1 A few times a month 1 8.3 8.3%
## 2 A few times a year 3 25 25%
## 3 Once every few years 8 66.7 66.7%
# Participants' previous globe experience distribution chart
ggplot(demographic.globes_exp, aes(x = "", y = count, fill = `Previous globes experience`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = graph_label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants Previous AR/VR Experience") +
theme_void() # Participants' previous Apple Vision Pro Experience distribution
demographic.visionpro_exp <- demographic %>%
select(UserID, Have_used_VisionPro) %>%
distinct() %>%
group_by(Have_used_VisionPro) %>%
summarise(count = n()) %>%
mutate(
percentage = round(count / sum(count) * 100, 1),
graph_label = paste0(percentage, "%")
) %>%
rename(`Have used Apple Vision Pro` = Have_used_VisionPro)
demographic.visionpro_exp## # A tibble: 2 × 4
## `Have used Apple Vision Pro` count percentage graph_label
## <chr> <int> <dbl> <chr>
## 1 I have never used the Apple Vision Pro 11 91.7 91.7%
## 2 I have used the Apple Vision Pro once or twice 1 8.3 8.3%
# Participants' previous Apple Vision Pro Experience distribution chart
ggplot(demographic.visionpro_exp, aes(x = "", y = count, fill = `Have used Apple Vision Pro`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = graph_label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants Previous AR/VR Experience") +
theme_void() data.positioning <- data %>%
mutate(positionCondition = if_else(rotateGlobeWhileDragging, "rotatingGlobe", "nonRotatingGlobe")) %>%
select(-rotateGlobeWhileDragging) %>%
inner_join(demographic, by = "UserID") %>%
inner_join(positioning_NRG, by = "UserID") %>%
rename(
PAAS_NRG = Mentally_demanding,
BORG_NRG = Physically_demanding
) %>%
mutate(
PAAS_NRG = as.numeric(str_extract(PAAS_NRG, "\\d+(\\.\\d+)?")),
BORG_NRG = as.numeric(str_extract(BORG_NRG, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_NRG = if_else(positionCondition == "nonRotatingGlobe", PAAS_NRG, NA_real_),
BORG_NRG = if_else(positionCondition == "nonRotatingGlobe", BORG_NRG, NA_real_)
) %>%
inner_join(positioning_RG, by = "UserID") %>%
rename(
PAAS_RG = Mentally_demanding,
BORG_RG = Physically_demanding
) %>%
mutate(
PAAS_RG = as.numeric(str_extract(PAAS_RG, "\\d+(\\.\\d+)?")),
BORG_RG = as.numeric(str_extract(BORG_RG, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_RG = if_else(positionCondition == "rotatingGlobe", PAAS_RG, NA_real_),
BORG_RG = if_else(positionCondition == "rotatingGlobe", BORG_RG, NA_real_)
) %>%
inner_join(positioning_preference, by = "UserID") %>%
rename(
behaviour_preference = Positioning_preference,
behaviour_feedback = Positioning_feedback
) %>%
mutate(
behaviour_preference = case_when(
str_detect(behaviour_preference, "Static orientation") ~ "staticOrientation",
str_detect(behaviour_preference, "Adaptive orientation") ~ "adaptiveOrientation",
str_detect(behaviour_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
) ) %>%
filter(Type == "positionTask") %>%
select(UserID, TaskID, ActionID, positionCondition, distance, direction, Date, ActionStatus, main_translation_x,
main_translation_y, main_translation_z, target_translation_x, target_translation_y, target_translation_z,
match_accuracy_result, status, PAAS_NRG, BORG_NRG, PAAS_RG, BORG_RG, behaviour_preference, behaviour_feedback) %>%
mutate(distance = as.factor(distance),
direction = as.factor(direction),
positionCondition = as.factor(positionCondition),
status = as.factor(status),
behaviour_preference = as.factor(behaviour_preference))##
## Shapiro-Wilk normality test
##
## data: data.positioning.accuracy$Accuracy
## W = 0.97029, p-value = 2.086e-09
hist(data.positioning.accuracy$Accuracy, breaks = 100,
main = "Histogram of Positioning Gestures Accuracy", xlab = "Accuracy",
col = "lightblue", xlim = c(0, 0.06))plot(density(data.positioning.accuracy$Accuracy),
main = "Density Plot of Positioning Gestures Accuracy", xlab = "Accuracy",
col = "blue", lwd = 2, xlim = c(0, 0.08))Although the w value is close to 1, the p value is below 0.05 so we reject null hypothesis that the data is normally distributed So, we cannot use one way ANOVA, instead, we use ART ANOVA test
data.positioning.matched <- data.positioning %>%
filter(status == "Matched")
data.positioning.matched.accuracy_avg.long <- data.positioning.matched %>%
group_by(UserID, positionCondition, distance, direction) %>%
summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE), .groups = 'drop')
data.positioning.matched.art <- art(mean_accuracy ~ positionCondition * distance * direction + (1|UserID), data = data.positioning.matched.accuracy_avg.long)
anova(data.positioning.matched.art)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(mean_accuracy)
##
## F Df Df.res Pr(>F)
## 1 positionCondition 0.88851 1 99.000 0.3481756
## 2 distance 8.58240 1 99.545 0.0042088 **
## 3 direction 3.51399 2 99.527 0.0335248 *
## 4 positionCondition:distance 0.88433 1 99.000 0.3493079
## 5 positionCondition:direction 0.21943 2 99.000 0.8033694
## 6 distance:direction 0.25114 2 99.512 0.7784052
## 7 positionCondition:distance:direction 1.62425 2 99.000 0.2022653
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • The main effect of positionCondition was not statistically significant: F(1, 99) = 0.89, p = 0.348.
# • The main effect of distance was statistically significant: F(1, 99.55) = 8.58, p = 0.0042 (**).
# • The main effect of direction was also statistically significant: F(2, 99.53) = 3.51, p = 0.0335 (*).
# • The interaction between positionCondition and distance was not significant: F(1, 99) = 0.88, p = 0.349.
# • The interaction between positionCondition and direction was not significant: F(2, 99) = 0.22, p = 0.803.
# • The interaction between distance and direction was not significant: F(2, 99.51) = 0.25, p = 0.778.
# • The three-way interaction (positionCondition × distance × direction) was not significant: F(2, 99) = 1.62, p = 0.202.
# There were significant main effects of both distance and direction on mean accuracy, indicating that participants’ accuracy was influenced independently by how far the task was and from which direction it came. However, positionCondition had no significant effect, and no interaction terms reached statistical significance. This suggests that the combined effects of position, distance, and direction do not differentially impact accuracy beyond the main effects of distance and direction alone.
# Plotting for position behaviours
data.positioning.matched.accuracy_avg.long %>%
group_by(positionCondition) %>%
summarise(
mean_mean_accuracy = mean(mean_accuracy, na.rm = TRUE),
se = sd(mean_accuracy, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
mutate(positionCondition = case_when(
positionCondition == "rotatingGlobe" ~ "Rotating Globe",
positionCondition == "nonRotatingGlobe" ~ "Non Rotating Globe",
TRUE ~ as.character(positionCondition))) %>%
ggplot(aes(x = positionCondition, y = mean_mean_accuracy, fill = positionCondition)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_accuracy - ci, ymax = mean_mean_accuracy + ci), width = 0.2) +
labs(
title = "Mean Match Accuracy by Globe Behaviour",
x = "Globe Behaviour",
y = "Mean Accuracy",
fill = "Globe Behaviour"
) +
theme_minimal() +
theme(axis.text.x = element_blank())# Plotting for distance conditions
data.positioning.matched.accuracy_avg.long %>%
group_by(distance) %>%
summarise(
mean_mean_accuracy = mean(mean_accuracy, na.rm = TRUE),
se = sd(mean_accuracy, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
ggplot(aes(x = distance, y = mean_mean_accuracy, fill = distance)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_accuracy - ci, ymax = mean_mean_accuracy + ci), width = 0.2) +
labs(
title = "Mean Match Accuracy by Globe Distance",
x = "Globe Distance",
y = "Mean Accuracy",
fill = "Globe Distance"
) +
theme_minimal() +
theme(axis.text.x = element_blank())# Plotting for direction conditions
data.positioning.matched.accuracy_avg.long %>%
group_by(direction) %>%
summarise(
mean_mean_accuracy = mean(mean_accuracy, na.rm = TRUE),
se = sd(mean_accuracy, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
ggplot(aes(x = direction, y = mean_mean_accuracy, fill = direction)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_accuracy - ci, ymax = mean_mean_accuracy + ci), width = 0.2) +
labs(
title = "Mean Match Accuracy by Globe Direction",
x = "Globe Direction",
y = "Mean Accuracy",
fill = "Globe Direction"
) +
theme_minimal() +
theme(axis.text.x = element_blank())# Plotting for both Position Conditions and Behaviours
data.positioning.matched.accuracy_avg.long %>%
group_by(positionCondition, distance, direction) %>%
summarise(
mean_mean_accuracy = mean(mean_accuracy, na.rm = TRUE),
se = sd(mean_accuracy, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
mutate(positionCondition = case_when(
positionCondition == "rotatingGlobe" ~ "Rotating Globe",
positionCondition == "nonRotatingGlobe" ~ "Non Rotating Globe",
TRUE ~ as.character(positionCondition))) %>%
ggplot(aes(x = positionCondition, y = mean_mean_accuracy, fill = positionCondition)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_accuracy - ci, ymax = mean_mean_accuracy + ci),
width = 0.2) +
facet_wrap(~ distance + direction) +
labs(
title = "Mean Match Accuracy by Globe Behaviour, Distance, and Direction",
x = "Globe Positioning Behaviour",
y = "Mean Accuracy",
fill = "Globe Positioning Behaviour"
) +
theme_minimal() +
theme(
axis.text.x = element_blank()
)# Boxplots for globe behaviour and conditions
ggplot(data.positioning.matched.accuracy_avg.long, aes(x = positionCondition, y = mean_accuracy)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ distance + direction) +
labs(title = "Boxplots of Accuracy by Globe Behaviour, Globes' Distance and Direction",
x = "Behaviour",
y = "Match Accuracy") +
theme_minimal()##
## Shapiro-Wilk normality test
##
## data: data.positioning.time$Time
## W = 0.59479, p-value < 2.2e-16
hist(data.positioning.time$Time, breaks = 100,
main = "Histogram of Positioning Gestures Task Completion Time", xlab = "Completion Time",
col = "lightblue", xlim = c(0, 1))plot(density(data.positioning.time$Time),
main = "Density Plot of Positioning Gestures Task Completion Time", xlab = "Completion Time",
col = "blue", lwd = 2, xlim = c(0, 1))data.positioning.taskCompletion_avg.long <- data.positioning %>%
group_by(UserID, positionCondition, TaskID, distance, direction) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
group_by(UserID, positionCondition, distance, direction) %>%
summarise(
avg_completion_time = mean(completion_time),
.groups = "drop"
)
data.positioning.taskCompletion_avg.art <- art(avg_completion_time ~ positionCondition * distance * direction + (1|UserID), data = data.positioning.taskCompletion_avg.long)
anova(data.positioning.taskCompletion_avg.art)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(avg_completion_time)
##
## F Df Df.res Pr(>F)
## 1 positionCondition 0.61363 1 99.002 0.4352944
## 2 distance 1.75116 1 100.619 0.1887297
## 3 direction 6.38814 2 100.863 0.0024424 **
## 4 positionCondition:distance 0.24125 1 99.002 0.6243926
## 5 positionCondition:direction 0.50478 2 99.002 0.6051833
## 6 distance:direction 0.57384 2 100.707 0.5651868
## 7 positionCondition:distance:direction 0.43726 2 99.002 0.6470467
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • positionCondition had no significant effect on average task completion time:F(1, 99.00) = 0.61, p = 0.435
# • distance also showed no significant effect:F(1, 100.62) = 1.75, p = 0.189
# • direction showed a statistically significant main effect:F(2, 100.86) = 6.39, p = 0.0024 (**), suggesting direction influences how long tasks take
# • The interaction between positionCondition and distance was not significant:F(1, 99.00) = 0.24, p = 0.624
# • The interaction between positionCondition and direction was not significant:F(2, 99.00) = 0.50, p = 0.605
# • The interaction between distance and direction was not significant:F(2, 100.71) = 0.57, p = 0.565
# • The three-way interaction (positionCondition × distance × direction) was also not significant:F(2, 99.00) = 0.44, p = 0.647
#
# Only direction had a statistically significant effect on average task completion time, indicating that the direction from which the task was approached meaningfully influenced how long participants took to complete it. Other factors—positionCondition, distance, and all interaction terms—did not have significant effects. This suggests that regardless of position or distance, direction alone may account for differences in task completion time in this context.
# Plotting for positioning behaviour
data.positioning.taskCompletion_avg.long %>%
group_by(positionCondition) %>%
summarise(
mean_mean_time = mean(avg_completion_time, na.rm = TRUE),
se = sd(avg_completion_time, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
mutate(positionCondition = case_when(
positionCondition == "rotatingGlobe" ~ "Rotating Globe",
positionCondition == "nonRotatingGlobe" ~ "Non Rotating Globe",
TRUE ~ as.character(positionCondition))) %>%
ggplot(aes(x = positionCondition, y = mean_mean_time, fill = positionCondition)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_time - ci, ymax = mean_mean_time + ci), width = 0.2) +
labs(
title = "Mean Match Accuracy by Globe Behaviour",
x = "Globe Behaviour",
y = "Average Completion Time",
fill = "Globe Behaviour"
) +
theme_minimal() +
theme(axis.text.x = element_blank())# Plotting for distance factors
data.positioning.taskCompletion_avg.long %>%
group_by(distance) %>%
summarise(
mean_mean_time = mean(avg_completion_time, na.rm = TRUE),
se = sd(avg_completion_time, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
ggplot(aes(x = distance, y = mean_mean_time, fill = distance)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_time - ci, ymax = mean_mean_time + ci), width = 0.2) +
labs(
title = "Mean Match Accuracy by Globe Distance Factors",
x = "Globe Distance",
y = "Average Completion Time",
fill = "Globe Distance"
) +
theme_minimal() +
theme(axis.text.x = element_blank())# Plotting for direction factors
data.positioning.taskCompletion_avg.long %>%
group_by(direction) %>%
summarise(
mean_mean_time = mean(avg_completion_time, na.rm = TRUE),
se = sd(avg_completion_time, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
ggplot(aes(x = direction, y = mean_mean_time, fill = direction)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_time - ci, ymax = mean_mean_time + ci), width = 0.2) +
labs(
title = "Mean Match Accuracy by Globe Direction Factors",
x = "Globe Direction",
y = "Average Completion Time",
fill = "Globe Direction"
) +
theme_minimal() +
theme(axis.text.x = element_blank())# Plotting for both Position Factors and Technique
data.positioning.taskCompletion_avg.long %>%
group_by(positionCondition, distance, direction) %>%
summarise(
mean_mean_time = mean(avg_completion_time, na.rm = TRUE),
se = sd(avg_completion_time, na.rm = TRUE) / sqrt(n()),
ci = qt(0.975, df = n() - 1) * se,
.groups = "drop"
) %>%
mutate(positionCondition = case_when(
positionCondition == "rotatingGlobe" ~ "Rotating Globe",
positionCondition == "nonRotatingGlobe" ~ "Non Rotating Globe",
TRUE ~ as.character(positionCondition))) %>%
ggplot(aes(x = positionCondition, y = mean_mean_time, fill = positionCondition)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = mean_mean_time - ci, ymax = mean_mean_time + ci),
width = 0.2) +
facet_wrap(~ distance + direction) +
labs(
title = "Mean Match Accuracy by Globe Behaviour, Distance, and Direction Factors",
x = "Globe Positioning Behaviour",
y = "Average Completion Time",
fill = "Globe Positioning Behaviour"
) +
theme_minimal() +
theme(
axis.text.x = element_blank()
)# Boxplots for Globe Behaviour, Distance, and Direction Factors
ggplot(data.positioning.taskCompletion_avg.long, aes(x = positionCondition, y = avg_completion_time)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ distance + direction) +
labs(title = "Boxplots of Task Completion Time by Behaviour, Distance, and Directio",
x = "Behaviour",
y = "Task Completion Time") +
theme_minimal()data.positioning.combined_exertion <- data.positioning.paas %>%
full_join(data.positioning.borg, by = c("UserID", "Technique")) %>%
pivot_longer(
cols = c(PAAS, BORGRPE),
names_to = "Measure",
values_to = "Score"
) %>%
mutate(
Measure = case_when(
Measure == "PAAS" & Technique == "rotatingGlobe" ~ "PAAS_RG",
Measure == "PAAS" & Technique == "nonRotatingGlobe" ~ "PAAS_NRG",
Measure == "BORGRPE" & Technique == "rotatingGlobe" ~ "BORG_RG",
Measure == "BORGRPE" & Technique == "nonRotatingGlobe" ~ "BORG_NRG",
TRUE ~ Measure
),
ExertionType = case_when(
str_detect(Measure, "PAAS") ~ "Cognitive load",
str_detect(Measure, "BORG") ~ "Physical exertion",
TRUE ~ "Unknown"
)
) %>%
select(UserID, Technique, Measure, Score, ExertionType) %>%
mutate(
Technique = as.factor(Technique),
ExertionType = as.factor(ExertionType)
)
data.positioning.combined_exertion.art_anova <- art(Score ~ Technique * ExertionType + (1|UserID), data = data.positioning.combined_exertion)
anova(data.positioning.combined_exertion.art_anova)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(Score)
##
## F Df Df.res Pr(>F)
## 1 Technique 12.54732 1 33 0.0012077 **
## 2 ExertionType 12.80843 1 33 0.0010921 **
## 3 Technique:ExertionType 0.17619 1 33 0.6773922
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • Main effect of positionCondition: F(1, 33) = 12.55, p = 0.0012 This is statistically significant, indicating that task positioning condition (e.g., rotating vs non-rotating globe) has a significant effect on the overall exertion score (which includes cognitive load and physical exertion).
# • Main effect of ExertionType: F(1, 33) = 12.81, p = 0.0011 Also statistically significant, meaning that there is a clear difference between cognitive load (PAAS) and physical exertion (BORG) scores regardless of the positioning condition.
# • Interaction effect (positionCondition × ExertionType): F(1, 33) = 0.18, p = 0.6774, suggesting that the effect of positioning condition does not depend on the type of exertion, and vice versa. The differences in PAAS and BORG scores are consistent across both positioning conditions.
# Both the positioning condition and type of exertion significantly influence the exertion score independently, but there is no interaction effect. This means that while both factors matter, their effects do not amplify or diminish each other. For example, rotating vs non-rotating globes affects scores, and cognitive vs physical exertion shows distinct levels, but the difference between PAAS and BORG scores remains similar across positioning conditions.
ggplot(data.positioning.combined_exertion, aes(x = Technique, y = Score, fill = ExertionType)) +
geom_boxplot(width = 0.1, position = position_dodge(0.8)) +
labs(
x = "Position Condition",
y = "Score",
title = "Distribution of Exertion Scores by Behaviour and Exertion Type"
) +
theme_minimal()data.positioning.combined_exertion %>%
group_by(Technique, ExertionType) %>%
summarise(
mean_score = mean(Score),
sd = sd(Score),
n = n(),
ci = qt(0.975, df = n - 1) * sd / sqrt(n),
.groups = "drop"
) %>%
ggplot(aes(x = Technique, y = mean_score, fill = ExertionType)) +
geom_col(position = position_dodge(width = 0.7), width = 0.6) +
geom_errorbar(aes(ymin = mean_score - ci, ymax = mean_score + ci),
position = position_dodge(width = 0.7), width = 0.2) +
labs(
x = "Position Technique",
y = "Mean Score (95% CI)",
fill = "Exertion Type",
title = "Mean Score by Position Condition and Exertion Type"
) +
theme_minimal()data.positioning.qualitative %>%
count(Positioning_preference) %>%
mutate(
percent = n / sum(n),
ncount = paste0(n, "\n", percent_format()(percent))
) %>%
ggplot(aes(x = "", y = n, fill = Positioning_preference)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +
labs(
title = "Distribution of Positioning Behaviour Preferences",
fill = "Preference"
) +
theme_void()
Comments
Summary
Study: Rotating
Rotating Data Preparation
Rotation Task Study
Accuracy
Normality
Although the w value is close to 1, the p value is below 0.05 so we reject null hypothesis that the data is normally distributed So, we cannot use one way ANOVA, instead, we use Wilcoxon signed-rank test
Statistical tests
Completion Time
Normality
Statistical Tests
Subjective Measures
Physical and Mental Exertion
Preference
Comments
Summary
Study: Scale
Scale Data Preparation
Scale Task Study
Accuracy
Normality
Statistical tests
Completion Time
Normality
Statistical Tests
Subjective Measures
Physical and Mental Exertion
Preference
Comments
Summary
Study: Combined Gesture Preference and Feedback